import {
  FontLoader,
  Material,
  Mesh,
  MeshBasicMaterial,
  Object3D,
  OrthographicCamera,
  TextGeometry,
  Vector3Tuple,
  Vector3,
  Quaternion,
  Matrix4,
} from 'three';
import { KeepSizeObject3D } from '@amcax/base';
import { isEqual } from 'lodash';
import gentilisRegular from 'three/examples/fonts/gentilis_regular.typeface.json';

export type CSTextAttrs = {
  position: Vector3Tuple;
  direction: Vector3Tuple;
  color?: number;
  needKeepSize?: boolean;
  size?: number;
  height?: number;
  text?: string;
  camera?: OrthographicCamera;
  renderOrder?: number;
  translate?: Vector3Tuple;
};

export class CSText extends KeepSizeObject3D {
  mesh: Mesh;
  factor: number;
  needKeepSize: boolean;
  camera: OrthographicCamera;

  private direction: Vector3Tuple;
  private positonVector: Vector3Tuple;
  private color: number;
  private geometry: TextGeometry;

  constructor(attrs: CSTextAttrs) {
    super();
    this.camera = attrs.camera;
    this.needKeepSize = attrs.needKeepSize;

    this.update(attrs);
  }

  setColor(color: number) {
    (this.mesh.material as MeshBasicMaterial).setValues({
      color,
    });
  }

  update({
    needKeepSize = true,
    translate = [0, 0, 0],
    direction,
    position,
    color,
    text,
    size = 1,
    height = 0.05,
    renderOrder,
  }: CSTextAttrs) {
    if (this.needKeepSize !== needKeepSize) {
      this.needKeepSize = needKeepSize;
      if (!this.needKeepSize) {
        this.scale.set(1, 1, 1);
      }
    }

    this.renderOrder = renderOrder;

    if (
      isEqual(this.direction, direction) &&
      isEqual(this.positonVector, position) &&
      color === this.color
    ) {
      return;
    }

    this.mesh?.geometry?.dispose();
    (this.mesh?.material as Material)?.dispose();

    this.direction = direction;
    this.positonVector = position;
    this.color = color;

    const font = new FontLoader().parse(gentilisRegular);
    this.geometry = new TextGeometry(text, {
      font: font,
      size: size,
      height: height,
    });

    // 根据 direction 旋转几何体，使文字正面朝向 direction
    const directionVector = new Vector3(
      direction[0],
      direction[1],
      direction[2],
    ).normalize();

    // 文字几何体默认朝向 -Z 方向
    const defaultDirection = new Vector3(0, 1, 0);

    // 计算从默认方向到目标方向的旋转四元数
    const quaternion = new Quaternion();
    quaternion.setFromUnitVectors(defaultDirection, directionVector);

    // 将四元数转换为旋转矩阵并应用
    const rotationMatrix = new Matrix4();
    rotationMatrix.makeRotationFromQuaternion(quaternion);
    this.geometry.applyMatrix4(rotationMatrix);

    this.geometry.translate(translate[0], translate[1], translate[2]);
    this.position.set(...position);

    const material = new MeshBasicMaterial({
      color: this.color,
      depthTest: false,
    });

    this.mesh = new Mesh(this.geometry, material);
    this.add(this.mesh);
  }

  addTo(object: Object3D) {
    object?.add(this);
  }

  destroy() {
    this.parent?.remove(this);
    this.mesh.geometry.dispose();
    (this.mesh.material as Material).dispose();
  }
}
